package com.xiyuan.smartutils.client.http.ws.impl;

import com.xiyuan.smartutils.Files;
import com.xiyuan.smartutils.client.http.ws.WebSocketPacket;
import com.xiyuan.smartutils.client.http.ws.WebSocketPacket.PacketHeader;
import com.xiyuan.smartutils.constants.WebSocketConstants;

import java.nio.ByteBuffer;

/**
 * 数据包解析器
 *
 * @author xiyuan-lgz 2024-11-03 @version v1.0.0 新建与整理
 */
public class WebSocketPacketResolver implements WebSocketConstants {
    private static final WebSocketBufferManager MANAGER_DEF = new WebSocketBufferManager().setEnableMiddleFrameEvent(
            true);
    
    private static PacketHeader parseHeader(ByteBuffer buffer) {
        if (buffer == null) {throw new IllegalArgumentException("Data is null.");}
        int length = buffer.remaining();
        if (length < 2) {return null;}
        
        // 1. check buffer[0]
        int b1 = buffer.get();
        int b2 = buffer.get();
        
        // 2.fin (fragmented packet)
        boolean fin = (b1 & FIN_MASK) != 0;
        
        // 3. rsv1, rsv2, rsv3 (reserved bits)
        if ((b1 & (RSV1_MASK | RSV2_MASK | RSV3_MASK)) != 0) {
            throw new IllegalArgumentException("Found nonzero bit in reserve field. (RSV1,2,3)");
        }
        
        // 4. opcode (operation code)
        int opcodeNum = b1 & OPCODE_MASK;
        Opcode opcode = Opcode.of(opcodeNum);
        if (opcode == null) {throw new IllegalArgumentException("Found illegal opcode " + opcodeNum + ".");}
        
        // 5. mask (masking bit)
        boolean mask = (b2 & MASK_MASK) != 0;
        
        // 6. payload len
        byte plt = (byte) (b2 & PAYLOAD_LEN_MASK);
        PayloadLength payloadLength = PayloadLength.of(plt);
        if (payloadLength == null) {
            throw new IllegalArgumentException("Found illegal payload length " + plt + ".");
        }
        if (length < 2 + payloadLength.offset()) {return null;}
        
        long dataLen = plt;
        switch (payloadLength) {
            case LEN_16:
                dataLen = ((buffer.get() & 0xFF) << 8) | (buffer.get() & 0xFF);
                break;
            case LEN_63:
                dataLen = MAX_FRAME_LENGTH_63 & buffer.getLong();
                break;
            default:
        }
        
        if (dataLen > Integer.MAX_VALUE) {throw new IllegalArgumentException("large data is not support yet");}
        
        PacketHeader header = new PacketHeader(opcode, (int) dataLen, payloadLength);
        header.setMask(mask);
        return header;
    }
    
    private static ByteBuffer readData(ByteBuffer netBuffer, PacketHeader header) {
        ByteBuffer data = ByteBuffer.allocate(header.getPayloadLength());
        if (!header.isMask()) {
            data.put(netBuffer);
        }
        else {
            byte[] maskingKey = new byte[4];
            netBuffer.get(maskingKey);
            int len = (int) header.getPayloadLength();
            for (int i = 0; i < len; i++) {
                data.put((byte) (netBuffer.get() ^ maskingKey[i % 4]));
            }
        }
        
        data.flip();
        return data;
    }
    
    private PacketState state = PacketState.DONE;
    
    public WebSocketPacket resolve(ByteBuffer buffer, WebSocketBufferManager manager) {
        if (manager == null) {
            manager = MANAGER_DEF;
        }
        PacketHeader header = parseHeader(buffer);
        if (header == null) {
            return null;
        }
        
        buffer = readData(buffer, header);
        
        // 没有开启存储
        if (manager.isEnableMiddleFrameEvent()) {
            return new WebSocketPacket(buffer, header);
        }
        
        // 合并数据
        buffer = manager.merge(buffer, header);
        if (header.isFragmented()) {
            // 中间数据包等下一在返回
            return null;
        }
        
        header.setPayloadLength(buffer.remaining());
        return new WebSocketPacket(buffer, header);
    }
    
    private enum PacketState {
        HEADER,
        FRAME,
        DONE;
    }
    
    public static void main(String[] args) {
        
        
        try {
            byte[] buffer = Files.read("D:/data/ws-packet");
            WebSocketPacket packet = new WebSocketPacketResolver().resolve(ByteBuffer.wrap(buffer), null);
            System.out.println(packet.getText());
            packet = new WebSocketPacket("QQ12311312131", MID);
            packet.mask();
            packet = new WebSocketPacketResolver().resolve(packet.toByteBuffer(), null);
            System.out.println(packet.getText());
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        
    }
}
